#ifndef WOLF_ENGINE_HPP
#define WOLF_ENGINE_HPP

#include <list>
#include <set>

#include "common.hpp"
#include "layer.hpp"
#include "Manager.hpp"

namespace wolf {
using namespace std;

#ifdef WOLF_DEBUG
# define WOLF_ENGINE_DEFAULT_MODE Engine::DEBUG
#else
# define WOLF_ENGINE_DEFAULT_MODE Engine::RELEASE
#endif

/**
 * @brief The main engine class.
 * @ingroup engineMod
 *
 *
 * This is the class that control the flow. It has to handle input from user
 *  and control how it is showed on the video. It should be just one instance.
 *  It provides a ways to user (a.k.a programmer) access the input and video (for now,
 *  I will call it just events) and extend it at will. It is called Event Handling system witch has low
 *  level access to the events control. To have a more high level access, look into
 *  classes Actor and Layer, and their related ActorManager and LayerManager. These classes
 *  uses the Engine interface underneath a more abstract and high level interface.
 *
 * @section EHS Event Handling System
 * The engine counts with a simple event handling system. It is based on the abstract class
 *  Manager. Any concrete subclass of it can receive the engine's events and handle it.
 *  There are seven event you can handle: SDL Events, Quit Events, Draw Events, Frame Events,
 *  Frame Start Events, Add Events and Remove Events.
 *
 * The events are divided on two categories, the Every-Frame events and the Responsive events. The
 *  first ones occurs exactly once per frame, and is intended to do refreshment, general logic
 *  code and drawing. The second occurs only in response at some external event.
 *
 * @subsection EHSEF Every-Frame Events
 *
 * The frame event is called in each frame, just after the SDL and quit handling and before the
 *  draw handling. It is handled by Manager::onFrame(), witch has also only the engine parameter.
 *  It returns nothing and it is not expected to do anything either. It is just an guaranteed every
 *  frame called event.
 *
 * The frame start event has same attributions of above event, except the moment it happens, which
 *  is just before SDL event (in fact it means it is before any events). It is handled by
 *  Manager::onStartFrame().
 *
 * The draw event is for draw thing on screen. It is the last event in a frame. It was decided
 *  because we want first do all logic (input and processing) and then present it on screen
 *  (the output). This event is handled by Manager::onDraw().
 *
 *
 * @subsection EHSR Responsive Events
 *
 * The SDL Events are handled by the method Manager::onEvent(). This method receives
 *  the engine and SDL_Event references. Except for the SDL_QUIT, all SDL_Events can be
 *  handled here. If the return value is true, the next handler (if any) receives the same event.
 *  Otherwise, if it is false the current event is considered handled, so the others handle are
 *  ignored and it pass to next event or to the frame event, if there is not any events left.
 *  It is the only reason to be allowed to you define the position to insert the handle on the
 *  addManager() method.
 *
 * The Quit event is handled by Manager::onQuit(). It receives only the engine. If the
 *  value returned is false, the exiting is aborted. Otherwise, it calls the next handle onQuit
 *  (if any) or just exit (if all handlers returned true). These events are called when it is
 *  detected an SDL_QUIT event, so if the exiting is called in any other way than that (as the
 *  exit() function or the end() method) these events are ignored.
 *
 * The add and remove events are respectivelly handled by Manager::onSetup() and Manager::onCleanup().
 *  They occur when a manager is added or removed (respectively) to/from engine. The Manager::onCleanup()
 *  is also called on the engine end() to all remaining handlers. The same occurs on start(), the
 *  engine call Manager::setup() to all managers added before it. So as the engine is not running, any
 *  manager added or removed before start() or after end() will not receive these events.
 *
 * @section MemoryManagement Memory Management
 * The memory management is based on giving to the user the freedom to decide how he/she is
 *  going to allocate and free the memory objects. So it does not alloc by itself or dealloc
 *  by itself. It is always supposed to the user to do it.
 *
 * The the Manager's add and remove behave according to this: The add always
 *  receive a reference instead a pointer to suggest that you can use either a staticly allocced
 *  variable or a dynamic one. Because of thar, neither remove method deletes it, they just
 *  removes it from the containers.
 *
 * To avoid segment fault from deleting a object before removing it, the destructor calls
 *  the respective remove methods. Of course, nothing could be done if you adds a reference
 *  invalid. (typically a pointer to already dead object, or non-initialized pointer). It also
 *  doesn't have no protection against memory leaks. If you lost a the external reference to an
 *  object, it could not be delete on any other way. Pay attention to it.
 *
 * With the screen surface it's a little bit different. As it is managed by the SDL code, we
 *  just have to make sure that the screen is the right surface. To ensure it the programmer
 *  must set the screen just after every SDL_SetVideoMode()'s call. Just because that, we provide
 *  the helper setVideoMode() method, which just does it for you.
 *
 **/
class Engine {
public:
	/**
	 * @brief The modes of running the Engine
	 **/
	enum RunMode {
		DEBUG, ///< If it is used the ESC, ALT-F4 and Close events are handled automatically and the program just close.
		RELEASE///< The programmer has total control of close events. The programmer is whom decides whenever close it.
	};

	static const int INSERT_AT_END = -1; ///< Use that to insert a handler at end of list
	static const int INSERT_AT_BEGIN = 0; ///< Use that to insert a handler at begin of list
	/**
	 * @brief Creates an Engine with the specified RunMode
	 *
	 * @param mode The run mode.
	 * @throw SingletonException if there is another engine
	 **/
	Engine(RunMode mode = DEBUG);
	/**
	 * @brief Just Kill it
	 *
	 * Call the end() inside it.
	 **/
	~Engine(void);
	/**
	 * @brief sets the video (define or resizes the screen)
	 *
	 * It calls SDL_SetVideoMode. It is equivalent to:
	 * @code
	 * engine.screen(SDL_SetVideoMode(width, height, bpp, flags));
	 * @endcode
	 *
	 * The flags are selected according witch best fit to the engine. So it
	 *  can change in future versions. By the way, actually it is: (SDL_HWSURFACE | SDL_ANYFORMAT).
	 *
	 * @param width
	 * @param height
	 * @param bpp The number of bits for each pixel. The 0 uses the system's bpp.
	 * @throw SdlError if it has some error on setting the video mode.
	 **/
	void setVideoMode(unsigned width, unsigned height, unsigned bpp = 0);
	/**
	 * @brief Changes the destiny surface
	 *
	 * It just use the gave surface as screen.
	 * @param screen The new surface to draw to.
	 **/
	void setScreen(SDL_Surface *screen);
	/**
	 * @brief Returns the screen's surface
	 *
	 * @return The surface where the staff are drawn.
	 **/
	SDL_Surface *getScreen();
	/**
	 * @brief Sets The Frame Rate
	 *
	 * @param fps The new number of pixel in a second to use.
	 **/
	void setFrameRate(unsigned fps);
	/**
	 * @brief Returns the Frame Rate
	 *
	 *
	 **/
	unsigned getFrameRate(void);
	/**
	 * @brief Responsible to starts all necessary SDL components and libraries
	 *
	 * It is an optional convenience, just to avoid the boring task to initialize
	 *  everything. Actually it just starts the SDL and TTF libraries.
	 * @throw SdlError if something could not be initialized.
	 **/
	static void prepare(void);
	/**
	 * @brief Starts the engine
	 *
	 * Everything the user must setup everything before call it.
	 * @throw EngineError if it has some error because a missing setting.
	 **/
	void start(void);
	/**
	 * @brief Ends Everything
	 *
	 * It is automatically called from destructor. This method doesn't call any
	 *  handler, so it guaranteed that the engine will be finalized. If you want
	 *  to execute the quitHandler's use halt() instead.
	 *
	 * It clears the layer's container, so after call it, you can restart without
	 *  any layer, but no handler is erase by that, so the handlers still the same.
	 * @see halt(), quitHandler
	 **/
	void end(void);
	/**
	 * @brief Returns if the engine is running
	 *
	 *
	 **/
	bool isRunning(void);
	/**
	 * @brief Runs a frame and give the rest for it.
	 **/
	void doFrame(void);
	/**
	 * @brief Gets the run mode
	 **/
	RunMode getRunMode(void);
	/**
	 * @brief Sets the run mode
	 *
	 *
	 **/
	void setRunMode(RunMode mode);
	/**
	 * @brief Returns the frame count
	 *
	 * @return The order number of current frame
	 **/
	unsigned getCurrentFrame(void);

	/**
	 * @brief Return the current frame time
	 *
	 * @return the milliseconds between the game begin and the current frame begin.
	 */

	unsigned getCurrentFrameTicks() const;
	/**
	 * @brief Gets the key state
	 *
	 * Just gets the keys state from SDL_GetKeyState().
	 **/
	Uint8 *getKeyState(int *numkeys = NULL);
	/**
	 * @brief Returns the current running instance of engine
	 *
	 *
	 **/
	inline static Engine & getInstance() {
		return *instance;
	}

	/**
	 * @brief Try to end the application
	 *
	 * Ask the application to halt. It will call the QuitEvents and if none of the
	 *  cancel the exiting, the program exits calling the end().
	 *
	 * @see end(), Manager.onQuit
	 **/
	void halt(void);
	/**
	 * @brief Adds a new manager
	 *
	 * The manager will not be deleted when the engine is finished. So you have
	 *  to keep track of your managers.
	 * @see @ref MemoryManagement
	 *
	 **/
	void addManager(Manager & eventHandler, int order = -1);
	/**
	 * @brief Removes a manager previously inserted
	 *
	 * If the manager was not inserted it just do nothing. This method is called
	 *  inside the manager's destructor so you did not have to worry about
	 *  segment fault or something like that.
	 * @see @ref MemoryManagement
	 **/
	void removeManager(Manager & eventHandler);
	/**
	 * @brief Returns if the manager has been added
	 *
	 *
	 **/
	bool hasManager(Manager & eventHandler);
private:
	bool hasLayer(Layer & layer);
	SDL_Surface *screen;
	unsigned frameRate;
	bool running;
	unsigned oldTime;
	unsigned frame;
	RunMode runMode;
	static Engine *instance;
	list<Manager*> handlers;
};

}

#endif // WOLF_ENGINE_HPP
